#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "optiondialog.h"
#include "aboutdialog.h"
#include "formhistroy.h"

#include <QSerialPort>
#include <QModbusTcpClient>
#include <QModbusRtuSerialClient>
#include <QStatusBar>
#include <QLabel>

//#define CARD_COUNT 11    // 1次性读取7张卡的数值

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow) {
    ui->setupUi(this);

    // 设置界面风格
    QSettings ini(QCoreApplication::applicationDirPath() + "/config.ini", QSettings::IniFormat, this);
    QStringList keyList = QStyleFactory::keys();
    QActionGroup* appStyleGroup = new QActionGroup(this);
    QString styleName = ini.value("sys/style").toString();

    for(int i = 0; i < keyList.length(); i++) // 当前系统支持的风格列表
    {
        QAction* appStyle = new QAction(this);  // 为每个风格建立一个菜单
        appStyle->setText(keyList.at(i));
        appStyle->setCheckable(true);
        connect(appStyle, &QAction::triggered, this, [ = ]()   // 风格菜单槽函数
        {
            QApplication::setStyle(QStyleFactory::create(appStyle->text()));
            QSettings ini(QCoreApplication::applicationDirPath() + "/config.ini", QSettings::IniFormat, this);
            ini.setValue("sys/style", appStyle->text());
        });      //关联槽函数
        ui->menuAppStyle->addAction(appStyle);
        appStyleGroup->addAction(appStyle);

        if(styleName == keyList.at(i))
        {
            appStyle->setChecked(true);
            QApplication::setStyle(QStyleFactory::create(styleName));
        }
    }

    set_max_col = ini.value("sys/col", 4).toInt();
    set_max_row = ini.value("sys/row", 2).toInt();
    set_volume = ini.value("sys/volume", 50).toInt();
    card_count = set_max_col * set_max_row;

    dbConnection();
    drawForm();
    ui->label_sb->setText(QString::number(card_count));

    // 通讯方式
    int typeindex = ini.value("network/type", 0).toInt();
    if(typeindex == 0)
        // 串口
        modbusDevice = new QModbusRtuSerialClient(this);
    else
        // TCP
        modbusDevice = new QModbusTcpClient(this);

    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(modbusDevice->errorString(), 10000);
    });


    if (!modbusDevice) {
        ui->actionConnect->setDisabled(true);
        statusBar()->showMessage(tr("Could not create Modbus client."), 10000);
    } else {
        connect(modbusDevice, &QModbusClient::stateChanged, this, &MainWindow::onModbusStateChanged);
    }

    // 轮询定时器
    readTimer = new QTimer(this);
    connect(readTimer, &QTimer::timeout, this, &MainWindow::readDataTimer);

    player = new QMediaPlayer;
    audioOutput = new QAudioOutput;
    player->setAudioOutput(audioOutput);
    player->setSource(QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/alarm.mp3"));

    // 状态栏：版权
    QLabel* Copyright = new
    QLabel("<font>Copyright&#169; 2019-2023</font> 武汉中祺科技", this);
    Copyright->setMinimumWidth(260);
    Copyright->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(Copyright); //现实永久信息
}

MainWindow::~MainWindow() {
    if (modbusDevice)
        modbusDevice->disconnectDevice();
    delete modbusDevice;

    delete ui;
}


void MainWindow::on_actionOption_triggered() {
    optionDialog *opt = new optionDialog();
    //顶端窗口
    opt->setWindowModality(Qt::ApplicationModal);
    opt->show();
}

// 连接网络
void MainWindow::on_actionConnect_triggered() {
    if (!modbusDevice)
        return;

    // 读取用户设定参数
    QSettings ini(QCoreApplication::applicationDirPath() + "/config.ini", QSettings::IniFormat, this);
    int typeindex = ini.value("network/type", 0).toInt();
    set_host     = ini.value("network/host", "127.0.0.1").toString();
    set_port     = ini.value("network/port", 5001).toInt();
    set_server   = ini.value("device/server", 99).toInt();      // 控制器地址
    set_register = ini.value("device/register", 501).toInt();   // 探测器地址
    set_poll_time = ini.value("device/polltime", 5).toInt();    // 轮询间隔时间
    set_number_of_retries = ini.value("device/numberofretries", 2).toInt(); // 读取超时重复读取次数

    statusBar()->clearMessage();
    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        if(typeindex == 0) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ini.value("network/serial", "COM1").toString());
            modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
            int t_baudrate = ini.value("network/baudrate", 9600).toInt();
            if(t_baudrate == 9600)
                modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
            else if(t_baudrate == 38400)
                modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud38400);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        } else {
            // 设置网络参数
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, set_host);
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, set_port);
        }
        modbusDevice->setTimeout(1000);
        modbusDevice->setNumberOfRetries(set_number_of_retries); // 超时重复读取次数

        if (!modbusDevice->connectDevice()) {
            // 连接失败
            statusBar()->showMessage(tr("Connect failed: %1").arg(modbusDevice->errorString()), 10000);
        } else {
            // 连接成功
            // 读取探测器数据
            on_actionReadData_triggered();
        }
    } else {
        // 关闭连接
        modbusDevice->disconnectDevice();
    }
}

// Modbus连接状态变化
void MainWindow::onModbusStateChanged(int state) {
    bool connected = (state != QModbusDevice::UnconnectedState);
    ui->actionConnect->setEnabled(!connected);
    ui->actionDisconnect->setEnabled(connected);
    ui->actionReadData->setEnabled(connected);

    QSettings ini(QCoreApplication::applicationDirPath() + "/config.ini", QSettings::IniFormat, this);
    int typeindex = ini.value("network/type", 0).toInt();
    QString serial = ini.value("network/serial", "COM1").toString();
    if(connected)
        statusBar()->showMessage(tr("成功连接: %1").arg(typeindex==0 ? serial : set_host), 10000);
    else
        statusBar()->showMessage(tr("连接关闭: %1").arg(typeindex==0 ? serial : set_host), 10000);

    // 当服务器断开后，就停止读取读取数据
    if(!connected && readTimer->isActive())
        readTimer->stop();
}

// 读取第 index 个探测器数据，index从1开始编号
void MainWindow::readData(int index) {
    if (!modbusDevice)
        return;

    statusBar()->clearMessage();
    qDebug() << QString("读取第%1号探测器数据.").arg(index);

    // 发送读取数据命令
    if (auto *reply = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters,
                      set_register + (index - 1) * 11,
                      11 * card_count /* 1个探测器数据有11个字节 */),
                      set_server)) {
        if (!reply->isFinished())
            // 接收到返回数据后，调用读取函数
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("读数据出错: %1").arg(modbusDevice->errorString()), 10000);
    }
}

// 读取并处理接收到的数据
void MainWindow::onReadReady() {
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        // 读取接收到的数据
        const QModbusDataUnit unit = reply->result();

        // 将收到的数据显示在界面上,如果serverAddress从1开始，就直接用w[reply->serverAddress()-1]
        int register_no, state, zc=0,gb=0,db=0,gz=0;
        for(int i=0; i<card_count; i++) {
            register_no = unit.value(i*11); // 寄存器号;
            if(register_no > 0) // 收到有效数据后，进行下一部处理
                state = ws[register_no - 1]->setData(unit.values().mid(i*11, 11));
            switch (state) {
            case 0x00:  // *****预热
                break;
            case 0x01:  // *****正常
                zc++;
                break;
            case 0x05:  // <<<<<<<<<<*****低报***********
                db++;
                break;
            case 0x06:  //  <<<<<<<<*****高报
                gb++;
                break;
            default:    // <<<<<<<<故障
                gz++;
                break;
            }
        }
        ui->label_zc->setText(QString::number(zc));
        ui->label_db->setText(QString::number(db));
        ui->label_gb->setText(QString::number(gb));
        ui->label_gz->setText(QString::number(gz));


        // 将16进制数据显示在调试窗口
        qDebug() << tr("接收(%1 bytes): ").arg(reply->result().valueCount()) << reply->rawResult().data().toHex(' ');

    } else if (
        // 数据读取出错
        reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("读响应错误: %1 (Modbus 异常: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->rawResult().exceptionCode(), -1, 16), 10000);
    } else {
        statusBar()->showMessage(tr("读响应错误: %1 (code: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->error(), -1, 16), 10000);
        qDebug() << tr("读响应错误: %1 (code: 0x%2)").
                 arg(reply->errorString()).
                 arg(reply->error(), -1, 16);
        // 设置界面超时状态
//        if(reply->error() == 0x05)
//            ws[register_index - 1]->setTimeoutState();
    }

    reply->deleteLater();
}


// 断开网络连接
void MainWindow::on_actionDisconnect_triggered() {
    modbusDevice->disconnectDevice();
    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
}


// 退出程序
void MainWindow::on_actionQuit_triggered() {
    this->close();
}

// 读取网络数据
void MainWindow::on_actionReadData_triggered() {
    if(readTimer->isActive()) {
        readTimer->stop();
        ui->actionReadData->setText("开始读取(&R)");
    } else {
        readTimer->start(set_poll_time * 1000);
        ui->actionReadData->setText("停止读取(&R)");
    }


}

// 在主页面中画探测器显示面板
void MainWindow::drawForm() {
    QSettings ini(QCoreApplication::applicationDirPath() + "/config.ini", QSettings::IniFormat, this);

    QGridLayout* grid = new QGridLayout();
    int x = set_max_col, y = set_max_row;

    used_list.clear();

    for(int i = 0; i < y; i++)
        for(int j = 0; j < x; j++) {
            int w_index = i * x + j;
            w[w_index] = new FormSub(set_server, w_index, this);    // 创建探测器显示界面，序号从0开始

            // 设置探测器是否启用
            bool tag = ini.value(QString("device/used%1").arg(w_index + 1), false).toBool();
//            w[w_index]->setUsedTag(tag);
            if(tag) {
                used_list.append(w_index + 1);  // 加入启用列表
                w[w_index]->setDescribe(ini.value(QString("device/describe%1").arg(w_index + 1), "未使用").toString());
            } else
                w[w_index]->setDescribe("未启用");

            // 探测器报警音信号
            connect(w[w_index], &FormSub::playWarningSound, this, &MainWindow::playWarningSound);
            connect(w[w_index], &FormSub::alarmDataChanged, this, &MainWindow::alarmDataChanged);

            w[w_index]->show();

            // 将探测器显示界面加入主界面
            grid->addWidget(w[w_index], i, j, 1, 1);

            // modbus地址跟FormSub关联，通过modbus地址访问FormSub
            // ws序号从0开始
            ws.insert(w_index, w[w_index]);
        }
    ui->groupBox_ws->setLayout(grid);

    tablemodel = new QSqlTableModel(this);
//    tablemodel->setTable("alarm");
    tablemodel->setQuery("select id,numtime,controller_addr,register_addr,desp,memo from alarm order by numtime desc limit 20");
    tablemodel->setEditStrategy(QSqlTableModel::OnManualSubmit);
    tablemodel->select();
    tablemodel->setHeaderData(0, Qt::Horizontal, tr("序号"));
    tablemodel->setHeaderData(1, Qt::Horizontal, tr("时间"));
    tablemodel->setHeaderData(2, Qt::Horizontal, tr("控制器地址"));
    tablemodel->setHeaderData(3, Qt::Horizontal, tr("探测器地址"));
    tablemodel->setHeaderData(4, Qt::Horizontal, tr("探测器描述"));
    tablemodel->setHeaderData(5, Qt::Horizontal, tr("报警事件内容"));

    ui->tableViewAlarm->setModel(tablemodel);
    ui->tableViewAlarm->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableViewAlarm->setColumnWidth(0, 60);
    ui->tableViewAlarm->setColumnWidth(1, 150);
    ui->tableViewAlarm->setColumnWidth(2, 90);
    ui->tableViewAlarm->setColumnWidth(3, 90);
    ui->tableViewAlarm->setColumnWidth(4, 120);
    ui->tableViewAlarm->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch);
    ui->tableViewAlarm->setAlternatingRowColors(true);
    ui->tableViewAlarm->verticalHeader()->setDefaultSectionSize(20);
    ui->tableViewAlarm->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);

    MyDelegate *myDele = new MyDelegate;
    ui->tableViewAlarm->setItemDelegate(myDele);
}

// 播放警告音
void MainWindow::playWarningSound() {
    if(alarmSound) {
        audioOutput->setVolume(set_volume);
        player->play();
    }
}

void MainWindow::MainWindow::alarmDataChanged()
{
    tablemodel->setQuery("select id,numtime,controller_addr,register_addr,desp,memo from alarm order by numtime desc limit 20");
}

// 定时读取每张卡的浓度数据
void MainWindow::readDataTimer() {
    /*  一次读一张卡，11个字节。
        if(used_list.count() == 0) {
            statusBar()->showMessage(tr("没有启用的探测器，请在设置菜单中选择."), 10000);
            return;
        }

        if(useb_list_index >= used_list.count()) { // 如果到最后一个探测器，就从第1个开始
            useb_list_index = 0; // 探测器的序号，从 1 开始编号。
        }

        register_index = used_list.at(useb_list_index);

        // 获取第 register_index 个探测器的数据
        readData(register_index);  // 参数为 1，代表第1个探测器
    //    qDebug() << tr("获取第%1号探测器数据").arg(register_index);

        // 手动添加测试数据。（测试用）
    //    w[register_index - 1]->setValus(register_index, 1, 8234, 1, 2, 6);

        useb_list_index++;  // 已启用探测器索引列表序号
    */

    /* 一次读7张卡的数值，77个字节 */
    readData(1);
}

void MainWindow::on_actionAbout_triggered() {
    AboutDialog* aboutdlg = new AboutDialog;
    aboutdlg->setWindowModality(Qt::ApplicationModal); //顶端窗口
    aboutdlg->show();
}


void MainWindow::on_actionAlarm_triggered() {
    playWarningSound();
//    dbConnection();
}

// 建立数据库连接，创建数据表
bool MainWindow::dbConnection() {
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(QCoreApplication::applicationDirPath() + "/db.sqlite3");

    if(!db.open()) {
        return false;
    }

    QSqlQuery query;

    // 创建报警记录表
    bool result = query.exec("CREATE TABLE IF NOT EXISTS alarm("
                             "    id integer primary key AUTOINCREMENT NOT NULL,"
                             "    controller_addr integer NOT NULL,"   // 控制器地址
                             "    register_addr integer NOT NULL,"   // 控制器地址
                             "    desp varchar(50) NOT NULL,"     // 探测器描述
                             "    numtime varchar(50) NOT NULL,"         // 报警时间
                             "    memo varchar(250))");                // 报警内容
    return result;
}

void MainWindow::on_actionHistory_triggered() {
    FormHistroy* histroy = new FormHistroy;
    histroy->show();
}


void MainWindow::on_actionactionAlarmCancel_triggered() {
    alarmSound = !alarmSound;
    if(alarmSound)
        ui->actionactionAlarmCancel->setText("关闭报警音(&C)");
    else
        ui->actionactionAlarmCancel->setText("打开报警音(&C)");
}

